package zj.xinxin.business.business.security;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;
import zj.xinxin.api.moudle.security.api.ConsumerService;
import zj.xinxin.api.moudle.security.dto.ConsumerDetailDto;
import zj.xinxin.api.moudle.security.dto.ConsumerInfoDto;
import zj.xinxin.api.moudle.security.dto.RoleDto;
import zj.xinxin.api.moudle.security.entity.ConsumerEntity;
import zj.xinxin.api.moudle.security.entity.ConsumerRoleEntity;
import zj.xinxin.api.moudle.security.entity.RoleEntity;
import zj.xinxin.api.moudle.security.vo.LoginVo;
import zj.xinxin.api.moudle.security.vo.UserInfoSearchVo;
import zj.xinxin.api.moudle.security.vo.UserInfoUpdateVo;
import zj.xinxin.api.moudle.security.vo.UserInfoVo;
import zj.xinxin.api.moudle.utils.ConsumerUtil;
import zj.xinxin.business.business.security.converter.ConsumerConverter;
import zj.xinxin.business.business.security.converter.RoleConverter;
import zj.xinxin.common.result.PageResult;
import zj.xinxin.mapper.mapper.security.ConsumerEntityMapper;
import zj.xinxin.mapper.mapper.security.ConsumerRoleEntityMapper;
import zj.xinxin.mapper.mapper.security.RoleEntityMapper;

import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @author junzhou
 * @date 2022/3/23 17:40
 * @since 1.8
 */
@Service
public class ConsumerServiceImpl extends ServiceImpl<ConsumerEntityMapper, ConsumerEntity> implements ConsumerService {

    @Autowired
    private PasswordEncoder passwordEncoder;

    @Autowired
    private ConsumerRoleEntityMapper consumerRoleMapper;

    @Autowired
    private RoleEntityMapper roleMapper;

    @Override
    public String userLogin(LoginVo loginVo) {
        System.out.println(loginVo);
        System.out.println(loginVo.getUsername());
        System.out.println(loginVo.getPassword());
        System.out.println(loginVo.getCaptcha());
        return null;
    }

    @Override
    @Transactional
    public Boolean addUser(UserInfoVo userInfo) {
        // 校验角色是否存在
        RoleEntity roleEntity = roleMapper.selectById(userInfo.getRoleId());
        if (roleEntity == null) {
            return Boolean.FALSE;
        }
        // 保存用户信息
        ConsumerEntity userEntity =
                ConsumerConverter.INSTANCE.toEntity(userInfo);
        userEntity.setPassword(passwordEncoder.encode(userEntity.getPassword()));

        boolean result = this.save(userEntity);
        Integer userId = userEntity.getUserId();
        if (userId == null || !result) {
            // 事务回滚
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            return Boolean.FALSE;
        }
        // 保存用户和角色对应关系
        int insertRes = consumerRoleMapper.insert(new ConsumerRoleEntity(userId, roleEntity.getRoleId()));
        if (insertRes != 1) {
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            return Boolean.FALSE;
        }
        return true;
    }

    @Override
    public PageResult<ConsumerInfoDto> getListByCurrentRoleId(long current, long size) {

        Integer currentRoleId = getCurrentUserRoleId();

        List<Integer> consumerIdList = consumerRoleMapper.selectConsumerIdList(currentRoleId);

        List<Integer> roleIdList = consumerRoleMapper.selectRoleIdList(currentRoleId);

        Map<Integer, RoleEntity> roleInfoMap = getRoleInfoList(roleIdList);

        Map<Integer, Integer> consumerRoleIdMap = getConsumerIdRoleIdMap(currentRoleId);

        Page<ConsumerEntity> consumerPage = baseMapper.selectPage(new Page<>(current, size),
                new LambdaQueryWrapper<ConsumerEntity>().in(ConsumerEntity::getUserId, consumerIdList));
        IPage<ConsumerInfoDto> infoDtoPage = consumerPage.convert(ConsumerConverter.INSTANCE::toDto)
                .convert(consumerInfoDto -> setRoleInfo(roleIdList, roleInfoMap, consumerRoleIdMap, consumerInfoDto));
        return new PageResult<>(infoDtoPage.getRecords(), infoDtoPage.getTotal());
    }

    @Override
    public PageResult<ConsumerInfoDto> searchUserInfo(UserInfoSearchVo searchVo) {

        Integer currentRoleId = getCurrentUserRoleId();

        List<Integer> consumerIdList = consumerRoleMapper.selectConsumerIdList(currentRoleId);

        List<Integer> roleIdList = consumerRoleMapper.selectRoleIdList(currentRoleId);

        Map<Integer, RoleEntity> roleInfoMap = getRoleInfoList(roleIdList);

        Map<Integer, Integer> consumerRoleIdMap = getConsumerIdRoleIdMap(currentRoleId);

        Page<ConsumerEntity> consumerPage = searchByCondition(searchVo, consumerIdList);
        IPage<ConsumerInfoDto> infoDtoPage = consumerPage.convert(ConsumerConverter.INSTANCE::toDto)
                .convert(consumerInfoDto -> setRoleInfo(roleIdList, roleInfoMap,
                        consumerRoleIdMap, consumerInfoDto));
        return new PageResult<>(infoDtoPage.getRecords(), infoDtoPage.getTotal());
    }

    @Override
    @Transactional
    public Boolean updateUser(UserInfoUpdateVo userInfo) {
        // 校验角色是否存在
        RoleEntity roleEntity = roleMapper.selectById(userInfo.getRoleId());
        if (roleEntity == null) {
            return Boolean.FALSE;
        }
        // 更新用户信息
        ConsumerEntity userEntity =
                ConsumerConverter.INSTANCE.toEntity(userInfo);
        if (StringUtils.isNotEmpty(userEntity.getPassword())){
            userEntity.setPassword(passwordEncoder.encode(userEntity.getPassword()));
        }
        boolean result = this.update(userEntity,
                new LambdaQueryWrapper<ConsumerEntity>()
                        .eq(ConsumerEntity::getUserId, userInfo.getUserId()));
        if (!result) {
            // 事务回滚
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            return Boolean.FALSE;
        }
        Integer roleId = consumerRoleMapper.selectMaxPermissionRoleId(userInfo.getUserId());
        if (!Objects.equals(roleId, userInfo.getRoleId())){
            // 保存用户和角色对应关系
            Boolean insertRes = consumerRoleMapper.updateConsumerRole(
                    new ConsumerRoleEntity(userInfo.getUserId(), userInfo.getRoleId()));
            if (!insertRes) {
                TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
                return Boolean.FALSE;
            }
        }
        return true;
    }

    @Override
    @Transactional
    public Boolean deleteUserInfo(Integer userId) {
        int delete = consumerRoleMapper.delete(new LambdaQueryWrapper<ConsumerRoleEntity>()
                .eq(ConsumerRoleEntity::getConsumerId, userId));
        if (delete == 0){
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
        }
        int result = this.baseMapper.deleteById(userId);
        return result != 0;
    }

    private Page<ConsumerEntity> searchByCondition(UserInfoSearchVo searchVo,
                                                   List<Integer> consumerIdList) {
        return baseMapper.selectPage(
                new Page<>(searchVo.getCurrent(), searchVo.getSize()),
                new LambdaQueryWrapper<ConsumerEntity>()
                        .in(ConsumerEntity::getUserId, consumerIdList)
                        .eq(searchVo.getIsDisable() != null,
                                ConsumerEntity::getIsDisable, searchVo.getIsDisable())
                        .and(StringUtils.isNotEmpty(searchVo.getName()),
                                wrapper -> {
                                    wrapper.like(ConsumerEntity::getUsername, searchVo.getName())
                                            .or()
                                            .like(ConsumerEntity::getNickname, searchVo.getName());
                                })
        );
    }

    private Map<Integer, Integer> getConsumerIdRoleIdMap(Integer currentRoleId) {
        List<Map<Integer, Integer>> consumerRoleIdRes = consumerRoleMapper.selectByTopRole(currentRoleId);
        Map<Integer, Integer> consumerRoleIdMap = new HashMap<>();
        consumerRoleIdRes.forEach(map -> {
            Integer conId = map.get("consumer_id");
            Integer roId = map.get("m_role_id");
            consumerRoleIdMap.put(conId, roId);
        });
        return consumerRoleIdMap;
    }

    private ConsumerInfoDto setRoleInfo(List<Integer> roleIdList,
                                        Map<Integer, RoleEntity> roleInfoMap,
                                        Map<Integer, Integer> consumerRoleIdMap,
                                        ConsumerInfoDto consumerInfoDto) {
        Integer roleId = consumerRoleIdMap.get(consumerInfoDto.getUserId());
        RoleEntity roleEntity = roleInfoMap.get(roleId);
        consumerInfoDto.setConsumerLevel(roleEntity.getRoleName());

        List<RoleDto> roleDtoList = roleIdList.stream()
                .filter(role -> role >= roleId)
                .map(role -> {
                    RoleEntity roleEntityOne = roleInfoMap.get(role);
                    return RoleConverter.INSTANCE.toDto(roleEntityOne);
                }).collect(Collectors.toList());
        consumerInfoDto.setRoleList(roleDtoList);
        return consumerInfoDto;
    }


    private Map<Integer, RoleEntity> getRoleInfoList(List<Integer> roleIdList) {
        return roleMapper.selectList(new LambdaQueryWrapper<RoleEntity>()
                        .in(RoleEntity::getRoleId, roleIdList))
                .stream()
                .collect(Collectors.toMap(RoleEntity::getRoleId, Function.identity()));

    }

    private Integer getCurrentUserRoleId() {

        ConsumerDetailDto userDetails = ConsumerUtil.getUserDetails();
        return userDetails.getRoleList().stream()
                .map(RoleDto::getRoleId)
                .min(Comparator.comparing(Function.identity()))
                .orElse(-1);
    }
}
